ποΈGitΠ―ΡΠ°ποΈ
docs/en/developer/measurement.md bf929c20f274232e0eb8ddc19b2dc82403e99e5b (bf929c20) Text, 5.47 KB
title: Measurement & Formatting
parent: Developer Guide
nav_order: 9
last_updated: 2026-05-13
aliases:
β’ measurement
β’ metric-formatter
β’ number-formatter
Measurement & Formatting
How the Meshtastic Android/KMP app formats numbers, units, and locale-sensitive values.
Overview
All measurement data transmitted by Meshtastic radios uses metric units (meters, Β°C, hPa, m/s, etc.). The app converts and formats these values for display using two core utilities:
βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β Utility β Location β Purpose β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€
β T383838MetricFormatter β T383838core/common/.../util/MetricFormatter.kt β Converts and formats physical measuβ¦ β
β T383838NumberFormatter β T383838core/common/.../util/NumberFormatter.kt β Low-level fixed-point number formatβ¦ β
βββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
Both live in T383838org.meshtastic.core.common.util and are available to all KMP targets (Android, Desktop, iOS).
MetricFormatter API
T383838MetricFormatter is a Kotlin T383838object with pure functions for each measurement type:
T282828
Tff7b72object T56d364MetricFormatter Tb4b4b4{
Tff7b72fun Td2a8fftemperatureTb4b4b4(Te6edf3celsiusTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3isFahrenheitTb4b4b4: Tffa657BooleanTb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffvoltageTb4b4b4(Te6edf3voltsTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff2Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffcurrentTb4b4b4(Te6edf3milliAmpsTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffpercentTb4b4b4(Te6edf3valueTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffhumidityTb4b4b4(Te6edf3valueTb4b4b4: Tffa657FloatTb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffpressureTb4b4b4(Te6edf3hPaTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffsnrTb4b4b4(Te6edf3valueTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffrssiTb4b4b4(Te6edf3valueTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffwindSpeedTb4b4b4(Te6edf3metersPerSecondTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffrainfallTb4b4b4(Te6edf3millimetersTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff1Tb4b4b4)Tb4b4b4: Tffa657String
Tb4b4b4}
Usage
T282828
T8b949e// Temperature β Fahrenheit conversion is handled automatically
Te6edf3MetricFormatterTb4b4b4.Te6edf3temperatureTb4b4b4(T79c0ff2T79c0ff2.5fTb4b4b4, Te6edf3isFahrenheit Tff7b72= Tff7b72trueTb4b4b4) T8b949e// "72.5Β°F"
Te6edf3MetricFormatterTb4b4b4.Te6edf3temperatureTb4b4b4(T79c0ff2T79c0ff2.5fTb4b4b4, Te6edf3isFahrenheit Tff7b72= Tff7b72falseTb4b4b4) T8b949e// "22.5Β°C"
T8b949e// Signal metrics
Te6edf3MetricFormatterTb4b4b4.Te6edf3snrTb4b4b4(Tff7b72-T79c0ff5.2fTb4b4b4) T8b949e// "-5.2 dB"
Te6edf3MetricFormatterTb4b4b4.Te6edf3rssiTb4b4b4(Tff7b72-T79c0ff9T79c0ff7Tb4b4b4) T8b949e// "-97 dBm"
T8b949e// Environment
Te6edf3MetricFormatterTb4b4b4.Te6edf3pressureTb4b4b4(T79c0ff1T79c0ff0T79c0ff1T79c0ff3.25fTb4b4b4) T8b949e// "1013.3 hPa"
Te6edf3MetricFormatterTb4b4b4.Te6edf3humidityTb4b4b4(T79c0ff6T79c0ff5.0fTb4b4b4) T8b949e// "65%"
Te6edf3MetricFormatterTb4b4b4.Te6edf3windSpeedTb4b4b4(T79c0ff3.7fTb4b4b4) T8b949e// "3.7 m/s"
Te6edf3MetricFormatterTb4b4b4.Te6edf3rainfallTb4b4b4(T79c0ff1T79c0ff2.3fTb4b4b4) T8b949e// "12.3 mm"
T8b949e// Power
Te6edf3MetricFormatterTb4b4b4.Te6edf3voltageTb4b4b4(T79c0ff3.95fTb4b4b4) T8b949e// "3.95 V"
Te6edf3MetricFormatterTb4b4b4.Te6edf3currentTb4b4b4(T79c0ff1T79c0ff2T79c0ff5.0fTb4b4b4) T8b949e// "125.0 mA"
NumberFormatter
T383838NumberFormatter provides locale-independent decimal formatting using pure arithmetic (no T383838String.format or T383838DecimalFormat):
T282828
Tff7b72object T56d364NumberFormatter Tb4b4b4{
Tff7b72fun Td2a8ffformatTb4b4b4(Te6edf3valueTb4b4b4: Tffa657DoubleTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String
Tff7b72fun Td2a8ffformatTb4b4b4(Te6edf3valueTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String
Tb4b4b4}
β Why locale-independent? Meshtastic is a mesh networking app where consistency matters β sensor
β readings shared between nodes should look the same everywhere. T383838NumberFormatter always uses T383838. as
β the decimal separator.
Temperature Conversion
Temperature is the only measurement that performs a unit conversion. The T383838isFahrenheit flag is typically sourced from the user's device locale or preferences:
T282828
Β°F = Β°C Γ 1.8 + 32
All other measurements display in their native metric units. The user-facing T383838units-and-locale.md page explains what end users see.
Adding a New Measurement Type
To add a new measurement formatter:
1. Add a function to T383838MetricFormatter in T383838core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/MetricFormatter.kt:
T282828
Tff7b72fun Td2a8ffradiationTb4b4b4(Te6edf3microSievertsTb4b4b4: Tffa657FloatTb4b4b4, Te6edf3decimalPlacesTb4b4b4: Tffa657Int Tff7b72= T79c0ff2Tb4b4b4)Tb4b4b4: Tffa657String Tff7b72=
Ta5d6ff"Tffd700${Te6edf3NumberFormatterTb4b4b4.Te6edf3formatTb4b4b4(Te6edf3microSievertsTb4b4b4, Te6edf3decimalPlacesTb4b4b4)Tffd700}Ta5d6ff ΞΌSv/hTa5d6ff"
2. Add tests in T383838core/common/src/commonTest/:
T282828
Tf0883e@Test
Tff7b72fun Td2a8ffradiationFormattingTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff0.15 ΞΌSv/hTa5d6ff"Tb4b4b4, Te6edf3MetricFormatterTb4b4b4.Te6edf3radiationTb4b4b4(T79c0ff0.15fTb4b4b4)Tb4b4b4)
Te6edf3assertEqualsTb4b4b4(Ta5d6ff"Ta5d6ff1.23 ΞΌSv/hTa5d6ff"Tb4b4b4, Te6edf3MetricFormatterTb4b4b4.Te6edf3radiationTb4b4b4(T79c0ff1.234fTb4b4b4)Tb4b4b4)
Tb4b4b4}
3. Use in UI β call from any T383838commonMain composable or ViewModel:
T282828
Te6edf3TextTb4b4b4(Te6edf3text Tff7b72= Te6edf3MetricFormatterTb4b4b4.Te6edf3radiationTb4b4b4(Te6edf3nodeTb4b4b4.Te6edf3radiationLevelTb4b4b4)Tb4b4b4)
4. Run verification:
T282828
./gradlew :core:common:allTests
DateFormatter
Date and time formatting uses the T383838DateFormatter interface with platform-specific implementations:
βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββ
β Function β Output Example β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββ€
β T383838formatRelativeTime() β "5 min ago" β
β T383838formatDateTime() β "May 13, 2026 2:30 PM" β
β T383838formatShortDate() β "May 13" β
β T383838formatTime() β "2:30 PM" β
β T383838formatTimeWithSeconds() β "2:30:45 PM" β
β T383838formatDate() β "2026-05-13" β
βββββββββββββββββββββββββββ΄βββββββββββββββββββββββββ
Unlike T383838MetricFormatter, T383838DateFormatter is an interface with platform T383838expect/T383838actual implementations because date formatting inherently depends on platform locale APIs.
Design Decisions
βββββββββββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Decision β Rationale β
βββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Locale-independent decimal separator (T383838.) β Mesh data shared between nodes must be consisteβ¦ β
β Pure arithmetic formatting (no T383838DecimalFormat) β Works identically on JVM, Native, and JS targets β
β Temperature is the only converted unit β All other metric units are universally understoβ¦ β
β T383838object singleton pattern β Stateless utility β no instance management needβ¦ β
βββββββββββββββββββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββββββββ
Related
β’ User-facing docs: T383838docs/user/units-and-locale.md explains what end users see
β’ Source code: T383838core/common/src/commonMain/kotlin/org/meshtastic/core/common/util/MetricFormatter.kt
β’ Tests: T383838core/common/src/commonTest/kotlin/org/meshtastic/core/common/util/MetricFormatterTest.kt
Served by rngit 1.5.0 - Generated in 0.06s